package cn.elwy.rcp.ui.table.provider;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.jface.resource.ColorDescriptor;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ITableColorProvider;
import org.eclipse.jface.viewers.ITableFontProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.model.IWorkbenchAdapter2;
import org.eclipse.ui.model.IWorkbenchAdapter3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.elwy.rcp.ui.table.adapter.TreeNodeLabelAdapter;
import cn.elwy.rcp.ui.table.adapter.TreeNodeStyleAdapter;
import cn.elwy.rcp.ui.table.model.TableInfo;
import cn.elwy.rcp.ui.util.AdapterUtil;
import cn.elwy.rcp.ui.util.ColorUtil;
import cn.elwy.rcp.ui.util.FontUtil;
import cn.elwy.rcp.ui.util.ImageUtil;

public class TableTreeLabelProvider extends LabelProvider implements ITableLabelProvider, ITableColorProvider,
    ITableFontProvider, IColorProvider, IFontProvider, IStyledLabelProvider {

  protected Logger logger = LoggerFactory.getLogger(this.getClass());

  protected List<Image> needDistroyImages = new LinkedList<Image>();
  protected List<Color> needDistoryColors = new LinkedList<Color>();
  private ResourceManager resourceManager;

  private TableInfo tableInfo;

  private IPropertyListener editorRegistryListener = new IPropertyListener() {
    public void propertyChanged(Object source, int propId) {
      if (propId == IEditorRegistry.PROP_CONTENTS) {
        fireLabelProviderChanged(new LabelProviderChangedEvent(TableTreeLabelProvider.this));
      }
    }
  };

  public TableTreeLabelProvider() {
    PlatformUI.getWorkbench().getEditorRegistry().addPropertyListener(editorRegistryListener);
  }

  public final String getText(Object element) {
    IWorkbenchAdapter adapter = AdapterUtil.getAdapter(element, IWorkbenchAdapter.class);
    if (adapter == null) {
      return "";
    }
    String label = adapter.getLabel(element);
    return decorateText(label, element);
  }

  @Override
  public String getColumnText(Object element, int columnIndex) {
    Object text = getValue(element, columnIndex);
    return text == null ? "" : text.toString();
  }

  public StyledString getStyledText(Object element) {
    IWorkbenchAdapter3 adapter = AdapterUtil.getAdapter(element, IWorkbenchAdapter3.class);
    if (adapter == null) {
      return new StyledString(getText(element));
    }
    StyledString styledString = adapter.getStyledText(element);
    // Now, re-use any existing decorateText implementation, to decorate
    // this styledString.
    String decorated = decorateText(styledString.getString(), element);
    Styler styler = getDecorationStyle(element);
    return StyledCellLabelProvider.styleDecoratedString(decorated, styler, styledString);
  }

  public Object getValue(Object element, int columnIndex) {
    TreeNodeLabelAdapter adapter = AdapterUtil.getAdapter(element, TreeNodeLabelAdapter.class);
    if (adapter != null) {
      return adapter.getColumnText(element, columnIndex, tableInfo);
    }
    Object[] texts = null;
    if (element instanceof Collection) {
      texts = (Object[]) ((Collection<?>) element).toArray();
    } else if (element instanceof Object[] || element instanceof Array) {
      texts = (Object[]) element;
    }
    if (texts != null && texts.length > columnIndex) {
      return texts[columnIndex];
    }
    return null;
  }

  protected String decorateText(String input, Object element) {
    return input;
  }

  protected Styler getDecorationStyle(Object element) {
    return StyledString.DECORATIONS_STYLER;
  }

  public final Image getImage(Object element) {
    IWorkbenchAdapter adapter = AdapterUtil.getAdapter(element, IWorkbenchAdapter.class);
    if (adapter == null) {
      return null;
    }
    ImageDescriptor descriptor = adapter.getImageDescriptor(element);
    if (descriptor == null) {
      return null;
    }
    descriptor = decorateImage(descriptor, element);
    return (Image) getResourceManager().get(descriptor);
  }

  protected ImageDescriptor decorateImage(ImageDescriptor input, Object element) {
    return input;
  }

  @Override
  public Image getColumnImage(Object element, int columnIndex) {
    TreeNodeStyleAdapter adapter = AdapterUtil.getAdapter(element, TreeNodeStyleAdapter.class);
    if (adapter == null) {
      return null;
    }
    String imagePath = adapter.getColumnImage(element, columnIndex, tableInfo);
    if (imagePath == null) {
      return null;
    }
    return ImageUtil.getImage(element.getClass(), imagePath);
  }

  public Color getBackground(Object element) {
    return getColor(element, false);
  }

  @Override
  public Color getBackground(Object element, int columnIndex) {
    return getColor(element, columnIndex, false);
  }

  public Color getForeground(Object element) {
    return getColor(element, true);
  }

  @Override
  public Color getForeground(Object element, int columnIndex) {
    return getColor(element, columnIndex, true);
  }

  private Color getColor(Object element, boolean forground) {
    IWorkbenchAdapter2 adapter = AdapterUtil.getAdapter(element, IWorkbenchAdapter2.class);
    if (adapter == null) {
      return null;
    }
    RGB descriptor = forground ? adapter.getForeground(element) : adapter.getBackground(element);
    if (descriptor == null) {
      return null;
    }
    return (Color) getResourceManager().get(ColorDescriptor.createFrom(descriptor));
  }

  private Color getColor(Object element, int columnIndex, boolean foreground) {
    TreeNodeStyleAdapter adapter = AdapterUtil.getAdapter(element, TreeNodeStyleAdapter.class);
    if (adapter == null) {
      return null;
    }
    String rgb = foreground ? adapter.getForeground(element, columnIndex, tableInfo) : adapter.getBackground(element,
        columnIndex, tableInfo);
    if (rgb != null) {
      return ColorUtil.getColor(rgb);
    }
    return null;
  }

  public Font getFont(Object element) {
    IWorkbenchAdapter2 adapter = AdapterUtil.getAdapter(element, IWorkbenchAdapter2.class);
    if (adapter == null) {
      return null;
    }
    FontData descriptor = adapter.getFont(element);
    if (descriptor == null) {
      return null;
    }
    return (Font) getResourceManager().get(FontDescriptor.createFrom(descriptor));
  }

  @Override
  public Font getFont(Object element, int columnIndex) {
    TreeNodeStyleAdapter adapter = AdapterUtil.getAdapter(element, TreeNodeStyleAdapter.class);
    if (adapter == null) {
      return null;
    }
    String font = adapter.getFont(element, columnIndex, tableInfo);
    if (font == null) {
      return null;
    }
    return FontUtil.getFont(font);
  }

  public void dispose() {
    PlatformUI.getWorkbench().getEditorRegistry().removePropertyListener(editorRegistryListener);
    if (resourceManager != null) {
      resourceManager.dispose();
    }
    resourceManager = null;
    super.dispose();
  }

  protected ResourceManager getResourceManager() {
    if (resourceManager == null) {
      resourceManager = new LocalResourceManager(JFaceResources.getResources());
    }
    return resourceManager;
  }

  public TableInfo getTableInfo() {
    return tableInfo;
  }

  public void setTableInfo(TableInfo tableInfo) {
    this.tableInfo = tableInfo;
  }

}
